package qc.common.core.utils;

import qc.common.core.enums.TimeRangeLengthEnum;

import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

/**
 * 时间日期处理工具类
 *
 * @author QuCheng Tech
 */
public class DateUtil extends org.apache.commons.lang3.time.DateUtils {
    // ==格式到年==
    /**
     * 日期格式，年份，例如：2004，2008
     */
    public static final String DATE_FORMAT_YYYY = "yyyy";

    // ==格式到年月 ==
    /**
     * 日期格式，年份和月份，例如：200707，200808
     */
    public static final String DATE_FORMAT_YYYYMM = "yyyyMM";

    /**
     * 日期格式，年份和月份，例如：200707，2008-08
     */
    public static final String DATE_FORMAT_YYYY_MM = "yyyy-MM";

    /**
     * 日期格式，年月日，例如：050630，080808
     */
    public static final String DATE_FORMAT_YYMMDD = "yyMMdd";

    /**
     * 日期格式，年月日，用横杠分开，例如：06-12-25，08-08-08
     */
    public static final String DATE_FORMAT_YY_MM_DD = "yy-MM-dd";

    /**
     * 日期格式，年月日，例如：20050630，20080808
     */
    public static final String DATE_FORMAT_YYYYMMDD = "yyyyMMdd";

    /**
     * 日期格式，年月日，用横杠分开，例如：2006-12-25，2008-08-08
     */
    public static final String DATE_FORMAT_YYYY_MM_DD = "yyyy-MM-dd";

    /**
     * 日期格式，年月日，例如：2016.10.05
     */
    public static final String DATE_FORMAT_POINTYYYYMMDD = "yyyy.MM.dd";

    /**
     * 日期格式，年月日，例如：2016年10月05日
     */
    public static final String DATE_TIME_FORMAT_YYYY年MM月DD日 = "yyyy年MM月dd日";

    /**
     * 日期格式，年月日时分，例如：200506301210，200808081210
     */
    public static final String DATE_FORMAT_YYYYMMDDHHmm = "yyyyMMddHHmm";

    /**
     * 日期格式，年月日时分，例如：20001230 12:00，20080808 20:08
     */
    public static final String DATE_TIME_FORMAT_YYYYMMDD_HH_MI = "yyyyMMdd HH:mm";

    /**
     * 日期格式，年月日时分，例如：2000-12-30 12:00，2008-08-08 20:08
     */
    public static final String DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI = "yyyy-MM-dd HH:mm";

    /**
     * 日期格式，年月日时分，例如：2000-12-30 12，2008-08-08 20
     */
    public static final String DATE_TIME_FORMAT_YYYY_MM_DD_HH = "yyyy-MM-dd HH";

    // ==格式到年月日 时分秒==
    /**
     * 日期格式，年月日时分秒，例如：20001230120000，20080808200808
     */
    public static final String DATE_TIME_FORMAT_YYYYMMDDHHMISS = "yyyyMMddHHmmss";

    /**
     * 日期格式，年月日时分秒，年月日用横杠分开，时分秒用冒号分开
     * 例如：2005-05-10 23：20：00，2008-08-08 20:08:08
     */
    public static final String DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS = "yyyy-MM-dd HH:mm:ss";

    // ==格式到年月日 时分秒 毫秒==
    /**
     * 日期格式，年月日时分秒毫秒，例如：20001230120000123，20080808200808456
     */
    public static final String DATE_TIME_FORMAT_YYYYMMDDHHMISSSSS = "yyyyMMddHHmmssSSS";

    // ==特殊格式==
    /**
     * 日期格式，月日时分，例如：10-05 12:00
     */
    public static final String DATE_FORMAT_MMDDHHMI = "MM-dd HH:mm";

    /**
     * 日期格式，年月日时分秒，例如：2000-12-30 12:00:00，2008-08-08 20:08:00
     */
    public static final String DATE_TIME_FORMAT_YYYYMMDDHHMM = "yyyy/MM/dd HH:mm";

    /**
     * 时间日期格式，在将String转换为Date时按列出的格式进行匹配转换
     */
    private static String[] DATE_PARSE_PATTERNS = {
            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm:ss.s", "yyyy-MM-dd HH:mm", "yyyy-MM",
            "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm:ss.s", "yyyy/MM/dd HH:mm", "yyyy/MM",
            "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm:ss.s", "yyyy.MM.dd HH:mm", "yyyy.MM"};

    /**
     * 获取当前Date型日期
     *
     * @return Date() 当前时间
     */
    public static Date getNowDate() {
        return new Date();
    }

    /**
     * 获取当日Date型日期，时分秒均为0
     *
     * @return Date() 当前日期
     */
    public static Date getTodayDate() {
        return getDayDate(getNowDate());
    }

    /**
     * 获取指定date的日期，时分秒均为0
     *
     * @param date 日期时间
     * @return java.util.Date
     * @author QuCheng Tech
     * @since 2023/12/20
     */
    public static Date getDayDate(Date date) {
        return newDate(getYear(date), getMonth(date), getDay(date), false);
    }

    /**
     * 根据指定的年月日得到日期，时分秒均为0；月份为0开始
     *
     * @return
     */
    public static Date newDate(int year, int month, int day) {
        return newDate(year, month, day, true);
    }

    /**
     * 根据指定的年月日得到日期，时分秒均为0；月份为0还是1开始可以传入
     *
     * @return
     */
    public static Date newDate(int year, int month, int day, boolean monthFromZero) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        if (monthFromZero)
            cal.set(Calendar.MONTH, month);
        else
            cal.set(Calendar.MONTH, month - 1);
        cal.set(Calendar.DAY_OF_MONTH, day);
        //设置时分秒，否则Calendar.getInstance()得到的是当前时间
        //cal.set(Calendar.HOUR, 0x0);//使用HOUR设置的时间为12点
        cal.set(Calendar.HOUR_OF_DAY, 0x0);
        cal.set(Calendar.MINUTE, 0x0);
        cal.set(Calendar.SECOND, 0x0);
        cal.set(Calendar.MILLISECOND, 0x0);

        return cal.getTime();
    }

    /**
     * 日期型字符串转化为日期 格式；已经做异常捕获处理，转换失败返回null
     */
    public static Date parseDate(Object obj) {
        if (obj == null) {
            return null;
        }
        Date date = null;
        try {
            if (obj instanceof Timestamp) {
                date = new Date(((Timestamp) obj).getTime());
            } else {
                date = parseDate(obj.toString(), DATE_PARSE_PATTERNS);
            }
        } catch (ParseException e) {
            //e.printStackTrace();
        }
        if (date == null) {
            //如果用格式数组转换得到的结果为null表示失败，在此使用中间带有T和Z的时间格式化方法进行一次转换
            try {
                //需要转换的时间
                //String myDateString = "2021-11-02T05:55:14.428Z";
                //进行转化时区
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.getDefault());
                date = dateFormat.parse(obj.toString().replace("Z", "+0000"));
            } catch (ParseException e) {
                //e.printStackTrace();
            }
            //中间带有T的转换
            if (date == null) {
                try {
                    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
                    date = dateFormat.parse(obj.toString());
                } catch (ParseException e) {
                    //e.printStackTrace();
                }
            }
            //中间带有T的转换
            if (date == null) {
                try {
                    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm", Locale.getDefault());
                    date = dateFormat.parse(obj.toString());
                } catch (ParseException e) {
                    //e.printStackTrace();
                }
            }
        }

        return date;
    }

    public static Date addYears(Date date, int years) {
        Calendar calendar = CalendarUtil.getCalendar(date);
        calendar = CalendarUtil.addYears(calendar, years);
        return CalendarUtil.getDate(calendar);
    }

    public static Date addMonths(Date date, int month) {
        Calendar calendar = CalendarUtil.getCalendar(date);
        calendar = CalendarUtil.addMonths(calendar, month);
        return CalendarUtil.getDate(calendar);
    }

    /**
     * 日期加n旬，返回的时间中日为1、11、21
     *
     * @param date    日期
     * @param tendays 旬数
     * @return java.util.Date
     * @author QuCheng Tech
     * @since 2024/5/9
     */
    public static Date addTendays(Date date, int tendays) {
        Calendar calendar = CalendarUtil.getCalendar(date);

        for (int i = 1; i <= tendays; i++) {
            int day = calendar.get(Calendar.DAY_OF_MONTH);
            if (day == 1) {
                calendar.set(Calendar.DAY_OF_MONTH, 11);
            } else if (day == 11) {
                calendar.set(Calendar.DAY_OF_MONTH, 21);
            } else if (day == 21) {
                //赋值为下个月的1号
                calendar.set(Calendar.DAY_OF_MONTH, 1);
                calendar = CalendarUtil.addMonths(calendar, 1);
            } else {
                //其他情况按照加10天
                calendar = CalendarUtil.addDays(calendar, 10);
            }
        }

        return CalendarUtil.getDate(calendar);
    }

    public static Date addDays(Date date, int days) {
        Calendar calendar = CalendarUtil.getCalendar(date);
        calendar = CalendarUtil.addDays(calendar, days);
        return CalendarUtil.getDate(calendar);
    }

    public static Date addHours(Date date, int hours) {
        Calendar calendar = CalendarUtil.getCalendar(date);
        calendar = CalendarUtil.addHours(calendar, hours);
        return CalendarUtil.getDate(calendar);
    }

    public static Date addMinutes(Date date, int minutes) {
        Calendar calendar = CalendarUtil.getCalendar(date);
        calendar = CalendarUtil.addMinutes(calendar, minutes);
        return CalendarUtil.getDate(calendar);
    }

    public static Date addSeconds(Date date, int seconds) {
        Calendar calendar = CalendarUtil.getCalendar(date);
        calendar = CalendarUtil.addSeconds(calendar, seconds);
        return CalendarUtil.getDate(calendar);
    }

    /***
     *
     * 获取日期格式化字符串，默认为yyyy-MM
     *
     * @param date  日期
     * @return java.lang.String
     * @author QuCheng Tech
     * @date 2024/7/8
     */
    public static String getDateMonthString(Date date) {
        return getString(date, DATE_FORMAT_YYYY_MM);
    }

    /***
     *
     * 获取日期格式化字符串，默认为yyyy-MM-dd
     *
     * @param date  日期
     * @return java.lang.String
     * @author QuCheng Tech
     * @date 2022/11/21
     */
    public static String getDateString(Date date) {
        return getString(date, DATE_FORMAT_YYYY_MM_DD);
    }

    /**
     * 根据指定的时段类型进行格式化输出
     *
     * @param date   日期时间
     * @param tmtype 时段类型
     * @return java.lang.String
     * @author QuCheng Tech
     * @since 2025/5/19
     */
    public static String getFormatString(Date date, TimeRangeLengthEnum tmtype) {
        //根据时段类型长度枚举进行不同处理
        switch (tmtype) {
            case HOUR:
                break;
            case DAY:
                return getString(date, "yyyy年MM月dd日");
            case WEEK:
                break;
            case TEN_DAYS:
                break;
            case MONTH:
                return getString(date, "yyyy年MM月");
            case QUARTER:
                break;
            case YEAR:
                return getString(date, "yyyy年");
            default://其他均当做以指定的分钟数为间隔长度
                return getDateTimeString(date);
        }

        return getDateTimeString(date);
    }

    /***
     *
     * 获取日期时间格式化字符串，默认为yyyy-MM-dd HH:mm:ss
     *
     * @param date  日期
     * @return java.lang.String
     * @author QuCheng Tech
     * @date 2022/11/21
     */
    public static String getDateTimeString(Date date) {
        return getString(date, DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS);
    }

    /***
     *
     * 获取日期格式化字符串
     *
     * @param date  日期
     * @return java.lang.String
     * @author QuCheng Tech
     * @date 2022/11/21
     */
    public static String getString(Date date, String pattern) {
        return parseDateToStr(date, pattern);
    }

    /**
     * 将指定的date按指定的格式化字符串进行输出
     * @param date, format
     * @return java.lang.String
     * @author QuCheng Tech
     * @date 2022/11/21
     */
    public static final String parseDateToStr(final Date date, final String format) {
        return new SimpleDateFormat(format).format(date);
    }

    /**
     * 获取某日期的年份
     *
     * @param date
     * @return
     */
    public static int getYear(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return cal.get(Calendar.YEAR);
    }

    /**
     * 获取某日期的月份，月份从1开始，返回1~12
     *
     * @param date 日期
     * @return
     */
    public static int getMonth(Date date) {
        return getMonth(date, true);
    }

    /**
     * 获取某日期的月份，月份为0开始，返回0~11
     *
     * @param date 日期
     * @return
     */
    public static int getMonthFromZero(Date date) {
        return getMonth(date, false);
    }

    /**
     * 获取某日期的月份，可以传入参数指定月份是从0开始还是从1开始
     *
     * @param date         日期
     * @param monthFromOne 月份是否从1开始
     * @return
     */
    public static int getMonth(Date date, boolean monthFromOne) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        if (monthFromOne)
            return cal.get(Calendar.MONTH) + 1;
        else
            return cal.get(Calendar.MONTH);
    }

    /**
     * 获取某日期的日数，每月的第1天从1开始
     *
     * @param date
     * @return
     */
    public static int getDay(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return cal.get(Calendar.DATE);//获取日
    }

    /**
     * 获取某日期的小时数，24小时制
     *
     * @param date
     * @return
     */
    public static int getHour(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return cal.get(Calendar.HOUR_OF_DAY);//获取小时数，从0开始
    }

    /**
     * 获取某日期的分钟数
     *
     * @param date
     * @return
     */
    public static int getMinute(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return cal.get(Calendar.MINUTE);//获取分钟数
    }

    /**
     * 获取某日期的秒数
     *
     * @param date
     * @return
     */
    public static int getSecond(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return cal.get(Calendar.SECOND);//获取秒数
    }

    /**
     * 获取某日期在一周中的序号，返回1-7，一周从星期日开始
     *
     * @param date
     * @return
     */
    public static int getDayOfWeek(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return cal.get(Calendar.DAY_OF_WEEK);
    }

    /**
     * 获取某日期在一周中的序号，返回1-7，可以指定一周是否从星期一开始
     *
     * @param date
     * @param isWeekFromMonday 一周是否从星期一开始
     * @return
     */
    public static int getDayOfWeek(Date date, boolean isWeekFromMonday) {
        int day = getDayOfWeek(date);
        if (isWeekFromMonday) {
            //day为1表示星期日，day为2表示星期一
            return day == 0x1 ? 0x7 : day - 0x1;
        }

        return day;
    }

    /**
     * 获取某个日期为星期几
     *
     * @param date
     * @return String "星期*"
     */
    public static String getDayWeekOfDate1(Date date) {
        String[] weekDays = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);

        int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (w < 0)
            w = 0;

        return weekDays[w];
    }

    /**
     * 获得两个时间的相差毫秒数
     *
     * @param startDate
     * @param endDate
     * @return
     */
    public static long getDistanceMilliSeconds(Date startDate, Date endDate) {
        long diffMilliSeconds = Math.abs(endDate.getTime() - startDate.getTime());
        return diffMilliSeconds;
    }

    /**
     * 获得两个时间的相差秒数
     *
     * @param startDate
     * @param endDate
     * @return
     */
    public static long getDistanceSeconds(Date startDate, Date endDate) {
        return getDistanceMilliSeconds(startDate, endDate) / 1000;
    }

    /**
     * 获得两个时间的相差分钟数
     *
     * @param startDate
     * @param endDate
     * @return
     */
    public static long getDistanceMinutes(Date startDate, Date endDate) {
        return getDistanceMilliSeconds(startDate, endDate) / 1000 / 60;
    }

    /**
     * 获得两个时间的相差小时数
     *
     * @param startDate
     * @param endDate
     * @return
     */
    public static long getDistanceHours(Date startDate, Date endDate) {
        return getDistanceMilliSeconds(startDate, endDate) / 1000 / 60 / 60;
    }

    /**
     * 获得两个时间的相差天数
     *
     * @param startDate
     * @param endDate
     * @return
     */
    public static long getDistanceDays(Date startDate, Date endDate) {
        return getDistanceMilliSeconds(startDate, endDate) / 1000 / 60 / 60 / 24;
    }

    /**
     * 获得两个日期的时间戳之差
     *
     * @param startDate
     * @param endDate
     * @return
     */
    public static Long getDistanceTimestamp(Date startDate, Date endDate) {
        long daysBetween = (endDate.getTime() - startDate.getTime() + 1000000) / (3600 * 24 * 1000);
        return daysBetween;
    }

    /**
     * 获得两个时间相差距离多少天多少小时多少分多少秒
     *
     * @param one 时间参数 1 格式：1990-01-01 12:00:00
     * @param two 时间参数 2 格式：2009-01-01 12:00:00
     * @return long[] 返回值为：{天, 时, 分, 秒}
     */
    public static long[] getDistanceTime(Date one, Date two) {
        long day = 0;
        long hour = 0;
        long min = 0;
        long sec = 0;
        try {

            long time1 = one.getTime();
            long time2 = two.getTime();
            long diff;
            if (time1 < time2) {
                diff = time2 - time1;
            } else {
                diff = time1 - time2;
            }
            day = diff / (24 * 60 * 60 * 1000);
            hour = (diff / (60 * 60 * 1000) - day * 24);
            min = ((diff / (60 * 1000)) - day * 24 * 60 - hour * 60);
            sec = (diff / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);
        } catch (Exception e) {
            e.printStackTrace();
        }
        long[] times = {day, hour, min, sec};
        return times;
    }
}
